package com.ztr.suanfa.aes;

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;

// 生成不同的秘钥，进行加解密
public class AesTestThree {

    private static final String ALGORITHM = "AES";

    private static final Charset UTF8 = StandardCharsets.UTF_8;

    public SecretKey geneKey() throws Exception {
        KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM);
        // 指定长度为128{128（24），256（32）}如果不指定长度，默认128
        keyGenerator.init(new SecureRandom());
        return keyGenerator.generateKey();
    }

    /**
     * 保存 AesKey 方法（可实现自己的处理逻辑）
     *
     * @param secretKey 秘钥
     * @throws IOException IO异常
     */
    public void savaAesKey(SecretKey secretKey) throws IOException {
        // 密码生成保存的地方
        File aesKeyFilesPath = new File("D://aesKeys");
        aesKeyFilesPath.mkdirs();
        File aesKeyFile = new File(aesKeyFilesPath, "aes.key");
        if (!aesKeyFile.exists()) {
            aesKeyFile.createNewFile();
        }
        Path aesKeyFilePath = Paths.get(aesKeyFile.getAbsolutePath());
        Files.write(aesKeyFilePath, secretKey.getEncoded());
    }

    /**
     * 读取文件中的AesKey
     *
     * @param filePath 秘钥文件路径
     * @return Aes秘钥
     * @throws IOException IO异常
     */
    public SecretKey readSecretKeyFromFile(String filePath) throws IOException {
        Path path = Paths.get(filePath);
        byte[] aesKeyBytes = Files.readAllBytes(path);
        return new SecretKeySpec(aesKeyBytes, ALGORITHM);
    }

    /**
     * 解密方法
     *
     * @param cipherStr Base64编码的加密字符串
     * @return 解密后的字符串(UTF8编码)
     * @throws Exception 异常
     */
    public String decrypt(String cipherStr) throws Exception {
        // step 1 获得一个密码器
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        // step 2 初始化密码器，指定是加密还是解密(Cipher.DECRYPT_MODE 解密; Cipher.ENCRYPT_MODE 加密)
        AesTestThree aesTestThree = new AesTestThree();
        // 初始化密码器
        cipher.init(Cipher.DECRYPT_MODE, aesTestThree.readSecretKeyFromFile("D:\\aesKeys\\aes.key"));
        // 对加密报文进行base64解码
        byte[] encrypted1 = Base64.decodeBase64(cipherStr);
        // 解密后的报文数组
        byte[] original = cipher.doFinal(encrypted1);
        // 输出utf8编码的字符串，输出字符串需要指定编码格式
        return new String(original, UTF8);
    }

    /**
     * 加密
     *
     * @param plainText 明文
     * @return Base64编码的密文
     * @throws Exception 加密异常
     */
    public String encrypt(String plainText) throws Exception {
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        AesTestThree aesTestThree = new AesTestThree();
        SecretKey secretKey = aesTestThree.geneKey();
        // 保存一下密码
        aesTestThree.savaAesKey(secretKey);
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        // 这里的编码格式需要与解密编码一致
        byte[] encryptText = cipher.doFinal(plainText.getBytes(UTF8));
        return Base64.encodeBase64String(encryptText);
    }

    public static void main(String[] args) {
        AesTestThree aesTestThree = new AesTestThree();
        String plainText = "z123456789";
        String cipherStr;
        try {
            SecretKey secretKey = aesTestThree.geneKey();
            // 对秘钥进行了Base64编码
            String base64AesKey = Base64.encodeBase64String(secretKey.getEncoded());
            System.out.println("生成的秘钥为[ " + base64AesKey + " ]");
            aesTestThree.savaAesKey(secretKey);
            System.out.println("被加解密的报文:[ " + plainText + " ]");
            cipherStr = aesTestThree.encrypt(plainText);
            System.out.println("AES 加密后的Base64报文:[ " + cipherStr + "]");
            System.out.println("对加密后的报文解密后的明文为:[ " + aesTestThree.decrypt(cipherStr) + " ]");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
